<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            background-color: #000;
        }

        .card {
            width: 200px;
            height: 250px;
            margin: 100px;
            transform: perspective(500px) rotateY(var(--ry, 0deg)) rotateX(var(--rx, 0deg));
            transition: all .3s ease-in-out;
            border-radius: 8px;
        }

        .card:hover {
            box-shadow: 0 0 20px rgba(255, 255, 255, 0.15);
        }
    </style>
</head>

<body>
    <img class="card"
        src="https://plus.unsplash.com/premium_photo-1677178660405-38e552adf46c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcwNjg2NDQyNQ&ixlib=rb-4.0.3&q=80&w=1080"
        alt="">
</body>
<script>
    const cardEl = document.querySelector(".card")
    const rangeX = [-20, 20]
    const rangeY = [-30, 30]
    const getRotate = (range, value, max) => {
        return value / max * (range[1] - range[0]) + range[0]
    }
    cardEl.onmousemove = (e) => {
        const { offsetX, offsetY } = e
        const { offsetWidth, offsetHeight } = cardEl
        const rx = getRotate(rangeX, offsetY, offsetHeight)
        const ry = -getRotate(rangeY, offsetX, offsetWidth)
        cardEl.style.setProperty('--rx', `${rx}deg`)
        cardEl.style.setProperty('--ry', `${ry}deg`)
    }
    cardEl.onmouseleave = (e) => {
        cardEl.style.setProperty('--rx', `0deg`)
        cardEl.style.setProperty('--ry', `0deg`)
    }
</script>

</html>